﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TextToHTMLExtensionInterfaces;
using System.ComponentModel.Composition;
using System.Net;
using System.IO;
using System.Diagnostics;

namespace EPSConverterLibrary
{
    /// <summary>
    /// Plug-in that does the EPS rendering
    /// </summary>
    [Export(typeof(IRenderToHTML))]
    [ExportMetadata("type", typeof(Uri))]
    public class EPSRender : IRenderToHTML
    {
        /// <summary>
        /// Render only if it is an eps file!
        /// </summary>
        /// <param name="objToRender"></param>
        /// <returns></returns>
        public bool CanRender(object objToRender)
        {
            Uri u = objToRender as Uri;
            if (u == null)
                return false;

            string t = u.AbsolutePath;
            int dot = t.LastIndexOf(".");
            if (dot < 0)
                return false;
            t = t.Substring(dot + 1);
            return t == "eps" || t == "ps";
        }

        /// <summary>
        /// Actually render this guy! Whoop!
        /// </summary>
        /// <param name="objToRender"></param>
        /// <returns></returns>
        public string Render(object objToRender)
        {
            Uri u = objToRender as Uri;

            string fname = u.AbsolutePath;
            int lastSlash = fname.LastIndexOf("/");
            if (lastSlash >= 0)
            {
                fname = fname.Substring(lastSlash + 1);
            }

            ///
            /// First step - download the eps file to our temp directory
            /// 

            WebResponse response = GetWebData(u);
            Stream output = response.GetResponseStream();

            FileInfo tempEPSFile = new FileInfo(Path.GetTempPath() + fname);
            using (Stream localFile = tempEPSFile.Create())
            {
                const int dataSize = 1024 * 8;
                byte[] data = new byte[dataSize];

                while (true)
                {
                    int bytesRead = output.Read(data, 0, dataSize);
                    if (bytesRead == 0)
                    {
                        break;
                    }
                    localFile.Write(data, 0, bytesRead);
                }
            }

            ///
            /// Now that the file is local, run the gs converter on it
            /// 

            FileInfo tempJPEGFile = new FileInfo(Path.ChangeExtension(tempEPSFile.FullName, ".jpg"));

            ProcessStartInfo sinfo = new ProcessStartInfo();
            sinfo.FileName = @"C:\Program Files\gs\gs8.63\bin\gswin32c.exe";
            sinfo.Arguments = "-sDEVICE=jpeg "
                + "-dJPEGQ=100 "
                + "-r120x120 "
                + "-sOutputFile=" + tempJPEGFile.Name + " "
                + "-dBATCH -dNOPAUSE "
                + tempEPSFile.Name;
            sinfo.CreateNoWindow = true;
            sinfo.RedirectStandardOutput = true;
            sinfo.UseShellExecute = false;
            sinfo.WorkingDirectory = tempEPSFile.DirectoryName;

            ///
            /// Now start the process. We switch the error mode
            /// so that when gs crashes it doesn't pop the dialog
            /// box. And because this is a process wide setting, we need
            /// to make sure two people don't try to start the process
            /// at once.
            /// 

            DateTime startime = DateTime.Now;
            Process gsproc = null;

            lock (this)
            {
                using (ChangeErrorMode newerr = new ChangeErrorMode(ChangeErrorMode.ErrorModes.FailCriticalErrors | ChangeErrorMode.ErrorModes.NoGpFaultErrorBox))
                {
                    gsproc = Process.Start(sinfo);
                    gsproc.PriorityClass = ProcessPriorityClass.Idle;
                }
            }

            ///
            /// Now monitor the i/o that comes back from there.
            /// 

            string error_seen = "";
            List<FileInfo> result = new List<FileInfo>();

            string line = "hi";
            while ((line = gsproc.StandardOutput.ReadLine()) != null)
            {
                line = line.Trim();
                if (line.StartsWith("Error:"))
                {
                    error_seen = line;
                }
            }

            ///
            /// Be good, wait for it to shut down - which should be seconds
            /// 

            gsproc.WaitForExit();
            return "<img src=\"" + tempJPEGFile.FullName + "\" />";
        }

        /// <summary>
        /// Store the username/passwords we get so we don't have to constantly re-ask them.
        /// </summary>
        private static CredentialCache credentialCache = new CredentialCache();

        /// <summary>
        /// Get the web data. If we fail because we are missing a username/password, ask!
        /// </summary>
        /// <param name="u"></param>
        /// <returns></returns>
        private static WebResponse GetWebData(Uri u)
        {
            try
            {
                return GetWebDataWithCurrentCache(u);
            }
            catch (WebException e)
            {
                if (e.Message.IndexOf("401") < 0)
                {
                    throw;
                }
            }

            ///
            /// Ok - need a username/password, I guess!
            /// 

            UserPass pw = new UserPass();
            if (pw.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                NetworkCredential cred = new NetworkCredential(pw.Username, pw.Password);
                credentialCache.Add(new Uri("http://" + u.Host), "Basic", cred);
                credentialCache.Add(new Uri("http://" + u.Host), "Digest", cred);
                credentialCache.Add(new Uri("http://" + u.Host), "Negotiate", cred);
                return GetWebData(u);
            }

            return null;
        }

        /// <summary>
        /// Get a stream that is the file data. For passwords/etc. use
        /// the current cache.
        /// </summary>
        /// <param name="u"></param>
        /// <returns></returns>
        private static WebResponse GetWebDataWithCurrentCache(Uri u)
        {
            WebResponse response = null;
            WebRequest r = WebRequest.Create(u);

            r.Credentials = credentialCache;
            response = r.GetResponse();
            return response;
        }
    }
}
